/**
 * =====================================================================================
 *       @file  ngx_event.c
 *      @brief  
 *
 *  Detailed description starts here.
 *
 *   @internal
 *     Created  2012年05月11日 12时13分12秒 
 *    Revision  1.0.0.0
 *    Compiler  gcc/g++
 *
 *     @author  luis
 * =====================================================================================
 */

#include <stdlib.h>
#include "ngx_event.h"
#include "ngx_core.h"
#include "ngx_conf_file.h"
#include "ngx_shmem.h"
#include "ngx_shmtx.h"
#include "ngx_cycle.h"
#include "ngx_palloc.h"
#include "ngx_connection.h"
#include "ngx_event_posted.h"

extern ngx_module_t *ngx_modules[];
extern ngx_module_t ngx_epoll_module;

static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle);
static ngx_int_t ngx_event_module_init(ngx_cycle_t *cycle);

ngx_atomic_t    *ngx_accept_mutex_ptr;
ngx_uint_t       ngx_use_accept_mutex;
ngx_int_t        ngx_accept_disabled;
ngx_int_t        ngx_accept_mutex_held;
ngx_int_t        ngx_accept_events;
ngx_shmtx_t      ngx_accept_mutex;

ngx_uint_t              ngx_event_flags;
ngx_event_actions_t     ngx_event_actions;

static ngx_str_t event_core_name = ngx_string("event_core");

static ngx_command_t ngx_events_commands[] =
{
    ngx_null_command
};

static ngx_core_module_t ngx_events_module_ctx =
{
    ngx_string("events"),
    NULL,
    NULL
};

ngx_module_t ngx_events_module =
{
    NGX_MODULE_V1,
    &ngx_events_module_ctx,
    ngx_events_commands,
    NGX_CORE_MODULE,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NGX_MODULE_V1_PADDING
};

static ngx_command_t ngx_events_core_commands[] =
{
    ngx_null_command
};

ngx_event_module_t ngx_event_core_module_ctx =
{
    &event_core_name,
    NULL,
    NULL,

    {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
};

ngx_module_t ngx_event_core_module = 
{
    NGX_MODULE_V1,
    &ngx_event_core_module_ctx,
    ngx_events_core_commands,
    NGX_EVENT_MODULE,
    NULL,
    ngx_event_module_init,
    ngx_event_process_init,
    NULL,
    NULL,
    NULL,
    NULL,
    NGX_MODULE_V1_PADDING 
};


static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle)
{
    ngx_uint_t           m;
    ngx_event_module_t  *module;
    ngx_event_t         *rev, *wev;
    ngx_connection_t    *c, *next;
    ngx_uint_t           i;
    ngx_listening_t     *ls;

    ngx_use_accept_mutex = 1;
    ngx_accept_mutex_held = 0;

    //XXX 调用所有event模块到事件init函数
    for (m = 0; ngx_modules[m]; m++)
    {
        if (ngx_modules[m]->type != NGX_EVENT_MODULE)
        {
            continue;
        }

        module = ngx_modules[m]->ctx;
        if (module->actions.init)
        {
            if (module->actions.init(cycle) != NGX_OK)
            {
                perror("init event module failed");
                exit(2);
            }
        }
    }

    //分配连接和读写事件
    cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n);
    if (cycle->connections == NULL)
    {
        return NGX_ERROR;
    }

    c = cycle->connections;

    cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n);
    if (cycle->read_events == NULL)
    {
        return NGX_ERROR; 
    }

    rev = cycle->read_events;
    for (i = 0; i < cycle->connection_n; ++i)
    {
        rev[i].closed = 1; 
        rev[i].instance = 1; 
    }

    cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n);
    if (cycle->write_events == NULL)
    {
        return NGX_ERROR; 
    }

    wev = cycle->write_events;
    for (i = 0; i < cycle->connection_n; ++i)
    {
        rev[i].closed = 1; 
    }
    
    i = cycle->connection_n;
    next = NULL;

    do 
    {
        i--;
        c[i].data = next;
        c[i].read = &cycle->read_events[i];
        c[i].write = &cycle->write_events[i];
        c[i].fd = (int)-1;

        next = &c[i];
    } while (i);

    cycle->free_connections = next;
    cycle->free_connection_n = cycle->connection_n;

    //给每个监听套接口分配一个连接
    ls = cycle->listening.elts; 
    for (i = 0; i < cycle->listening.nelts; ++i)
    {
        c = ngx_get_connection(ls[i].fd); 
        if (c == NULL)
        {
            return NGX_ERROR; 
        }

        c->listening = &ls[i];
        ls[i].connection = c;

        rev = c->read;

        rev->accept = 1;
        rev->handler = ngx_event_accept;
        if (ngx_use_accept_mutex)
        {
            continue; 
        }
    }
    
    return NGX_OK;
}

void ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
    ngx_uint_t flags;
    
    flags = 0;
    if (ngx_use_accept_mutex)
    {
        if (ngx_accept_disabled > 0)    //超过最大连接数到7/8,不能获得锁
        {
            ngx_accept_disabled--; 
        }
        else
        {
            if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR)
            {
                return; 
            }

            if (ngx_accept_mutex_held)
            {
                flags |= NGX_POST_EVENTS; 
            }
        }
    }

    (void)ngx_process_events(cycle, 10000000, flags);

    if (ngx_posted_accept_events)
    {
        ngx_event_process_posted(cycle, &ngx_posted_accept_events); 
    }

    if (ngx_accept_mutex_held)
    {
        ngx_shmtx_unlock(&ngx_accept_mutex); 
    }

    if (ngx_posted_events)
    {
        ngx_event_process_posted(cycle, &ngx_posted_events); 
    }
}

static ngx_int_t ngx_event_module_init(ngx_cycle_t *cycle)
{
    u_char              *shared;
    size_t               size, cl;
    ngx_shm_t            shm;

    cl = 128;    

    size = cl;           /* ngx_accept_mutex */

    shm.size = size;
    shm.name.len = sizeof("nginx_shared_zone");
    shm.name.data = (u_char *)"nginx_shared_zone";

    if (ngx_shm_alloc(&shm) != NGX_OK)
    {
        perror("ngx shm alloc failed");
        return NGX_ERROR;         
    }

    shared = shm.addr;
    ngx_accept_mutex_ptr = (ngx_atomic_t *)shared;
    ngx_accept_mutex.spin = (ngx_uint_t)-1;

    if (ngx_shmtx_create(&ngx_accept_mutex, (ngx_shmtx_sh_t *)shared) != NGX_OK)
    {
        perror("ngx shmtx create failed");
        return NGX_ERROR;
    }

    return NGX_OK;
}
